#pragma once

#include "RawBuf.h"
////////////////////////////////////////
// TSArray.
////////////////////////////////////////
template<class Type, u32_t Max> struct TSArray
{
	Type mElements[Max];

	Type& operator[](s32_t index)
	{
		_check(index < Max);
		return mElements[index];
	}

	u32_t Num()
	{
		return Max;
	}
};

////////////////////////////////////////
// TArray.
////////////////////////////////////////
template< class T > class TArray : public RawBuf
{
public:
	typedef T ElementType;

	TArray()
		:RawBuf()
	{}
	TArray( s32_t inNum )
		:RawBuf( inNum, sizeof(T) )
	{}
	TArray( const TArray& other )
		:RawBuf( other.mNum, sizeof(T) )
	{
		
		mNum=0;
		for( s32_t i=0; i<other.mNum; i++ )
		{
			Add( other[i]);
		}

	}
	~TArray()
	{
		_check(mNum>=0);
		_check(mCap>=mNum);
		RemoveAt( 0, mNum );
	}
	T& operator[]( s32_t i )
	{
		_check(i>=0);
		_check(mCap>=mNum);
		return ((T*)mBuf)[i];
	}
	const T& operator[]( s32_t i ) const
	{
		_check(i>=0);
		_check(i<=mNum);
		_check(mCap>=mNum);
		return ((T*)mBuf)[i];
	}
	T Pop()
	{
		_check(mNum>0);
		_check(mCap>=mNum);
		T Result = ((T*)mBuf)[mNum-1];
		RemoveAt( mNum-1 );
		return Result;
	}
	T& Last( s32_t c=0 )
	{
		_check(c<mNum);
		_check(mCap>=mNum);
		return ((T*)mBuf)[mNum-c-1];
	}
	const T& Last( s32_t c=0 ) const
	{
		_check(c<mNum);
		_check(mCap>=mNum);
		return ((T*)mBuf)[mNum-c-1];
	}
	void Shrink()
	{
		RawBuf::Shrink( sizeof(T) );
	}
	bool Find( const T& Item, s32_t& index ) const
	{
		for( index=0; index<mNum; index++ )
			if( (*this)[index]==Item )
				return 1;
		return 0;
	}
	s32_t FindIndex( const T& Item ) const
	{
		for( s32_t index=0; index<mNum; index++ )
			if( (*this)[index]==Item )
				return index;
		return null_index;
	}
	void Clear( s32_t cap=0 )
	{
		for( s32_t i=0; i<mNum; i++ )
			(&(*this)[i])->~T();
		RawBuf::Clear( sizeof(T), cap);
	}

	void Resize( s32_t num)
	{
		if( num > mNum)
		{
			for( s32_t i = mNum; i < num; ++i)
			{
				AddNew();
			}
		}
		else if( num < mNum)
		{
			RemoveAt( num, mNum - num);
		}
	}

	TArray& operator=( const TArray& other )
	{
		if( this != &other )
		{
			Clear( other.mNum );
			for( s32_t i=0; i<other.mNum; i++ )
			{
				Add( other[i] );
			}
		}
		return *this;
	}
	s32_t Add( const T& Item )
	{
		s32_t index = RawBuf::Add(1,sizeof(T));
		new( &((*this)[index])) T(Item);
		return index;
	}

	T& AddNew()
	{
		s32_t index = RawBuf::Add(1,sizeof(T));
		T* ret = new( &((*this)[index])) T;
		return *ret;
	}

	
	s32_t AddUnique( const T& Item )
	{
		for( s32_t index=0; index<mNum; index++ )
			if( (*this)[index]==Item )
				return index;
		return Add( Item );
	}
	s32_t Remove( const T& Item )
	{
		s32_t originalNum=mNum;
		for( s32_t index=0; index<mNum; index++ )
			if( (*this)[index]==Item )
				RemoveAt( index-- );
		return originalNum - mNum;
	}
	void RemoveAt( s32_t index, s32_t cnt=1 )
	{
		_check(index>=0);
		_check(index<=mNum);
		_check(index+cnt<=mNum);

		for( s32_t i=index; i<index+cnt; i++ )
			(&(*this)[i])->~T();

		RawBuf::RemoveAt( index, cnt, sizeof(T) );
	}

	// Iterator.
	class Iterator
	{
	public:
		Iterator( TArray<T>& InArray ) : mArray(InArray), mIndex(0) {}
		void Next()   { ++mIndex; }
		void RemoveCurrent()   { mArray.Remove(mIndex--); }
		s32_t Index()   const { return mIndex;}
		operator bool()const { return mIndex < mArray.Num();}
		T& operator*()const { return mArray[mIndex];}
		T* operator->()const { return &mArray[mIndex];}
		T& GetCurrent()const { return mArray[ mIndex ];}
	private:
		TArray<T>& mArray;
		s32_t mIndex;
	};
};


